/* --------------------------------------------------------------------------------
 * PropellentNet: Propeller IDE
 * Copyright 2008 - Jason Wood
 * --------------------------------------------------------------------------------
 * Editor.cs
 * User interface Code Editor.
 * --------------------------------------------------------------------------------
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * --------------------------------------------------------------------------------
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.IO; 

namespace PropellentNet
{
    public partial class Editor : UserControl
    {
        
        #region Privates
        StringBuilder m_paintString = new StringBuilder();

        Font m_DrawFont;

        SizeF m_CharSize;
        int m_CurrentChar = 0;
        int m_CurrentLine = 0;
        int m_CoursorChar = 10;
        int m_CoursoLine = 2;

        private Color m_colorCON = Color.FromArgb(253, 243, 168);
        private Color m_colorOBJ = Color.FromArgb(255, 191, 191);
        private Color m_colorVAR = Color.FromArgb(255, 223, 191);
        private Color m_colorPUB1 = Color.FromArgb(191, 223, 255);
        private Color m_colorPUB2 = Color.FromArgb(167, 210, 253);
        private Color m_colorPRI1 = Color.FromArgb(191, 248, 255);
        private Color m_colorPRI2 = Color.FromArgb(167, 243, 253);
        private Color m_colorDefault = Color.FromArgb(255, 248, 192);

        private ArrayList m_CodeRegions;

        Hashtable m_TextLines = new Hashtable();

        int[] m_Selection = new int[] {0, 0, 0, 0 };

        PropellentNet.SpinInterpreter m_Interpreter;

        #endregion Privates
        
        #region De/Constructor
        public Editor()
        {
            InitializeComponent();
        }
        #endregion De/Constructor
        
        #region Events
        
        private void Editor_Load(object sender, EventArgs e)
        {

            this.DoubleBuffered = true;
            
            m_DrawFont = new Font("Parallax", 10.0f);

            m_Interpreter = new SpinInterpreter(@"C:\temp\hold.spin");
            ReadFile();

        }

        private void Editor_MouseDown(object sender, MouseEventArgs e)
        {

            m_CoursorChar = (int)Math.Round(((e.X + -this.AutoScrollPosition.X) / m_CharSize.Width) + 1.25f, 3);
            m_CoursoLine = (int)Math.Ceiling((e.Y + -this.AutoScrollPosition.Y) / m_CharSize.Height);

            if (m_CoursorChar < 0)
                m_CoursorChar = 0;

            if (m_CoursoLine < 0)
                m_CoursoLine = 0;

            //int curLine = ((int[])m_TextLines[m_CoursoLine])[0];
            //if (m_CoursorChar > curLine)
            //    m_CoursorChar = curLine;

            m_Selection = new int[] { m_CoursorChar, m_CoursoLine, m_CoursorChar, m_CoursoLine };

            this.Invalidate();

        }
        private void Editor_MouseMove(object sender, MouseEventArgs e)
        {

            if (e.Button == MouseButtons.Left)
            {
                m_CoursorChar = (int)Math.Round(((e.X + -this.AutoScrollPosition.X) / m_CharSize.Width) + 1.25f, 0);
                m_CoursoLine = (int)Math.Ceiling((e.Y + -this.AutoScrollPosition.Y) / m_CharSize.Height);

                //int curLine = ((int[])m_TextLines[m_CoursoLine])[0];
                //if (m_CoursorChar > curLine)
                //    m_CoursorChar = curLine;

                if (m_CoursorChar < 0)
                    m_CoursorChar = 0;
                if (m_CoursoLine < 0)
                    m_CoursoLine = 0;

                m_Selection = new int[] { m_Selection[0], m_Selection[1], m_CoursorChar, m_CoursoLine };

                this.Invalidate();

            }

        }
        
        private void Editor_KeyDown(object sender, KeyEventArgs e)
        {

        }

        private void Editor_Paint(object sender, PaintEventArgs e)
        {

            // ((SpintCodeObject)m_Interpreter.SpinTypes[0]);
            Graphics drawIt = e.Graphics;
            
            Point curViewPorint = this.AutoScrollPosition;
            drawIt.TranslateTransform(curViewPorint.X, curViewPorint.Y);
            
            m_CharSize = drawIt.MeasureString("W", m_DrawFont);
            m_CharSize = new SizeF(m_CharSize.Width * 0.618f, m_CharSize.Height * 0.89f);
            char curChar;
            
            this.HorizontalScroll.SmallChange = (int)Math.Round(m_CharSize.Height, 2);
            this.HorizontalScroll.LargeChange = (int)Math.Round(m_CharSize.Height, 2) * 3;
            
            drawIt.Clear(m_colorDefault);
            
            Brush backGround = new SolidBrush(Color.Blue);
            Brush SelectedBackground = new SolidBrush(Color.Black);
            SolidBrush textBrush = new SolidBrush(Color.Black);
            SolidBrush selectedtextBrush = new SolidBrush(Color.White);
            
            float curY = 0.0f;
            float curX = 0.0f;
            float largestX = 255.0f;
            m_CurrentLine = 0;
            
            int curRegion = 0;
            int curRegCharIndex = (int)((object[])m_CodeRegions[curRegion])[0];
            SolidBrush currentArea = new SolidBrush(m_colorDefault);
            SolidBrush lastTextBrush = textBrush;
            Font lastTextFont = m_DrawFont;
            
            int currentCodeObject = 0;
            PropellentNet.SpinCodeObject CurrentObj = (PropellentNet.SpinCodeObject)m_Interpreter.CodeObjects[currentCodeObject];
            
            m_CurrentChar = 1;
            /// run through every char in the code for visual formating
            for (int i = 0; i < m_paintString.Length; i++)
            {
                
                #region Draw Region Colors
                /// We pre-fetched the indexes of the indivdual regions
                /// So now lets draw the appropriate color per region
                /// 
                /// Check each time if the current index is the beginning
                /// of the next region. If it is the next region then we
                /// need to increase the RegCharIndex to the next char index.

                if (i >= curRegCharIndex)
                {
                    if (curRegion < m_CodeRegions.Count)
                    {

                        currentArea = new SolidBrush((Color)((object[])m_CodeRegions[curRegion])[1]); /// get the current Regions background color   
                        curRegion++; /// increase the region index
                        if (curRegion < m_CodeRegions.Count) /// check that we haven't reached the end of the regions
                            curRegCharIndex = (int)((object[])m_CodeRegions[curRegion])[0];


                        drawIt.FillRectangle(currentArea, curX, curY, 255.0f * m_CharSize.Width, m_CharSize.Height); /// This line is the start of the region so color it's background

                    }
                }
                #endregion Draw Region Colors
                
                #region Draw Text
                curChar = m_paintString[i];

                //if (-m_CurrentLine + 1 == CurrentObj.EditorLocation.LineNumber && m_CurrentChar >= CurrentObj.EditorLocation.CharIndex)
                //{
                //    currentCodeObject += 1;
                //    if (currentCodeObject < m_Interpreter.CodeObjects.Count)
                //        CurrentObj = (PropellentNet.SpinCodeObject)m_Interpreter.CodeObjects[currentCodeObject];MO
                //    if (!m_DrawFont.Bold)
                //    {
                //        // lastTextBrush = textBrush;
                //        lastTextFont = m_DrawFont;
                //    }
                //    m_DrawFont = new Font("Parallax", 10.0f, FontStyle.Bold);
                //    // textBrush = new SolidBrush(Color.Yellow);
                //}
                //else
                //{
                //    m_DrawFont = lastTextFont;
                //    //textBrush = lastTextBrush;
                //}
                //drawIt.FillRectangle(SelectedBackground, curX, curY, m_CharSize.Width, m_CharSize.Height);
                
                switch (curChar)
                {
                    case '\t':
                        m_CurrentChar += 4;
                        curX += 4 * (m_CharSize.Width);
                        break;
                        
                    case '\n':
                        m_CurrentLine -= 1;
                        curY += m_CharSize.Height;
                        
                        if (curX > largestX)
                            largestX = curX;
                        
                        m_CurrentChar = 0;
                        curX = -m_CharSize.Width;
                        
                        drawIt.FillRectangle(currentArea, curX, curY, 255.0f * m_CharSize.Width, m_CharSize.Height);
                        
                        break;
                        
                    default:
                        if (curY >= (-curViewPorint.Y) - m_CharSize.Height && curY <= -((curViewPorint.Y) - this.Height))
                        {
                            
                            if (isSelected(m_CurrentChar, -m_CurrentLine))
                                drawIt.FillRectangle(SelectedBackground, curX, curY, m_CharSize.Width, m_CharSize.Height);
                            
                        }
                        break;
                        
                }
                
                #endregion Draw Text
                
                m_CurrentChar += 1;
                curX += m_CharSize.Width;
                
            }
            
            if (curX > largestX)
                largestX = curX;
            
            if (this.AutoScrollMinSize != new Size((int)largestX, (int)curY))
                this.AutoScrollMinSize = new Size((int)largestX, (int)(curY + m_CharSize.Height));
            
            int ViewTopLine = (int)((double)-this.AutoScrollPosition.Y / m_CharSize.Height);
            int ViewBottomLine = ViewTopLine + (int)(this.Height / m_CharSize.Height);
            
            foreach (PropellentNet.SpinCodeObject tmpCodeObj in m_Interpreter.CodeObjects)
            {
                if (tmpCodeObj.EditorLocation.LineNumber >= ViewTopLine && tmpCodeObj.EditorLocation.LineNumber <= ViewBottomLine)
                    tmpCodeObj.Draw(drawIt, m_CharSize);
            }
            
            drawIt.FillRectangle(new SolidBrush(Color.HotPink), (m_CoursorChar - 1) * m_CharSize.Width, (m_CoursoLine - 1) * m_CharSize.Height, 2, m_CharSize.Height);
            
        }
        
        #region Overrides
        protected override void OnMouseWheel(MouseEventArgs e)
        {
            base.OnMouseWheel(e);
        }
        #endregion Overrides

        #endregion Events
        
        #region Private Functions
        private void ReadFile()
        {

            TextReader tmpRead = (TextReader)new StreamReader(@"C:\temp\hold.spin");

            m_paintString.Append(tmpRead.ReadToEnd());

            tmpRead.Close();

            #region Parse out Blocks
            m_CodeRegions = new ArrayList();

            string holdStr = m_paintString.ToString();
            int currentIndex = 0;

            if ((currentIndex = holdStr.IndexOf("CON\r\n", StringComparison.OrdinalIgnoreCase)) > -1)
                m_CodeRegions.Add(new object[] { currentIndex, m_colorCON });

            if ((currentIndex = holdStr.IndexOf("OBJ\r\n", StringComparison.OrdinalIgnoreCase)) > -1)
                m_CodeRegions.Add(new object[] { currentIndex, m_colorOBJ });

            if ((currentIndex = holdStr.IndexOf("VAR\r\n", StringComparison.OrdinalIgnoreCase)) > -1)
                m_CodeRegions.Add(new object[] { currentIndex, m_colorVAR });

            int lastPub = 0;
            currentIndex = 0;
            while ((currentIndex = holdStr.IndexOf("PUB ", currentIndex + 1, StringComparison.OrdinalIgnoreCase)) > -1)
            {
                m_CodeRegions.Add(new object[] { currentIndex, (lastPub == 0 ? m_colorPUB1 : m_colorPUB2)});
                lastPub ^= 1;
            }

            currentIndex = 0;
            int lastPri = 0;
            while ((currentIndex = holdStr.IndexOf("PRI ", currentIndex + 1, StringComparison.OrdinalIgnoreCase)) > -1)
            {
                m_CodeRegions.Add(new object[] { currentIndex, (lastPri == 0 ? m_colorPRI1 : m_colorPRI2) });
                lastPri ^= 1;
            }

            CMySort tmpS = new CMySort(1);
            m_CodeRegions.Sort(tmpS);

            for (int i = 0; i < m_paintString.Length; i++)
            {

                if (m_paintString[i] == '\n')
                {
                    m_CurrentLine += 1;
                    m_TextLines.Add(m_CurrentLine, new int[] { m_CurrentChar - 1, i });
                    m_CurrentChar = 0;
                }

                m_CurrentChar += 1;


            }
            #endregion Parse out Blocks
            
        }
        
        private RectangleF SelectedArea()
        {
            
            RectangleF tmpRec = new RectangleF();

            

            return tmpRec;
            
        }
        private bool isSelected(int X, int Y)
        {

            int[] tmpSel = new int[] { 0, 0, 0, 0 };


            if (Y + 1 == m_Selection[1] && Y + 1 == m_Selection[3]) /// Is the current line on of the selected lines
            {
                if (m_Selection[0] > m_Selection[2]) /// Check the direction of the selection set : Left To Right
                {
                    if (X + 1 > m_Selection[2] && X  < m_Selection[0])
                        return true;
                }
                else if (m_Selection[0] < m_Selection[2]) /// Check the direction of the selection set : Right To Left
                {
                    if (X + 1 > m_Selection[0] && X  < m_Selection[2])
                        return true;
                }
            }
            else if (m_Selection[1] > m_Selection[3]) /// Check the direction of the selection set : Down To Up
            {
                if (Y + 1 > m_Selection[3] && Y + 1 < m_Selection[1])
                    return true;
            }
            else if (m_Selection[1] < m_Selection[3]) /// Check the direction of the selection set : Up To Down
            {
                if (Y + 1 > m_Selection[1] && Y + 1 < m_Selection[3])
                    return true;
            }
            else
            {
            }
            
            return false;

        }
        #endregion Private Functions
        
        #region Comparer
        class CMySort : IComparer
        {
            private int intCompType;
            public CMySort(int sortOrder)
            {
                intCompType = sortOrder;
            }
            public int Compare(object x, object y)
            {
                if ((int)(((object[])x)[0]) > (int)(((object[])y)[0]))
                    return 1;
                else if ((int)(((object[])x)[0]) < (int)(((object[])y)[0]))
                    return -1;
                else
                    return 0;
            }
        }
        #endregion Comparer
        
    }
}
